#include "catalog.h"
#include "query.h"
#include "sort.h"
#include "index.h"
#include <assert.h>

// Simple nested loops
Status Operators::SNL(const string & result,          // output relation name
	              const int projCnt,              // number of attributes in the projection
                      const AttrDesc attrDescArray[], // The projection list (as AttrDesc)
                      const AttrDesc & attrDesc1,     // The left attribute in the join predicate
                      const Operator op,              // The join operation
                      const AttrDesc & attrDesc2,     // The left attribute in the join predicate
                      const int reclen)               // The lenght of a tuple in the result relation
{
  cout << "Algorithm: Simple NL Join" << endl;

	Status lStatus;
	HeapFileScan outerScan(attrDesc2.relName, lStatus);
	if(lStatus != OK)
		return lStatus;

	HeapFileScan innerScan(attrDesc1.relName, lStatus);
	if(lStatus != OK)
		return lStatus;

	lStatus = outerScan.startScan(0, 0, (Datatype)NULL, NULL, NOTSET);
	if(lStatus != OK)
		return lStatus;

	HeapFile heapFileResult(result, lStatus);
	if(lStatus != OK)
		return lStatus;

	int result_len = 0;
	for(int i = 0; i < projCnt; i++)
	{
		result_len += attrDescArray[i].attrLen;
	}
	if(result_len != reclen)
		return BAD_REC_LEN;

	Status outer_scan_status = OK;
	Status inner_scan_status = OK;
	RID outerRID;
	RID innerRID;
	Record innerRec;
	Record outerRec;
	while(outer_scan_status != FILEEOF)
	{
		outer_scan_status = outerScan.scanNext(outerRID, outerRec);
		if(outer_scan_status == FILEEOF)
			break;

		if(outer_scan_status != OK)
			return inner_scan_status;

		//get filter val from outer scan and use it as the filter for the inner scan

		char filter_val[attrDesc2.attrLen];
		if(attrDesc2.attrOffset + attrDesc2.attrLen > outerRec.length)
		{
			return ATTRTOOLONG;
		}
		memcpy(filter_val, (char*)outerRec.data + attrDesc2.attrOffset, attrDesc2.attrLen); 

		//inner loop
		inner_scan_status = innerScan.startScan(attrDesc1.attrOffset, attrDesc1.attrLen, (Datatype)attrDesc1.attrType, 
			filter_val, op);	
		if(inner_scan_status != OK)
			return inner_scan_status;

		while(inner_scan_status != FILEEOF)
		{
			inner_scan_status = innerScan.scanNext(innerRID, innerRec);
			if(inner_scan_status == FILEEOF)
				break;
			if(inner_scan_status != OK)
				return inner_scan_status;	
			//insert the record
			Record result_rec;	
			result_rec.data = malloc(result_len);
			result_rec.length = result_len;
			int result_offset = 0;
			for(int i = 0; i < projCnt; i++)
			{
				//inner check
				if(strcmp(attrDesc1.relName, attrDescArray[i].relName) == 0)
				{
					//error check
					if(result_offset + attrDescArray[i].attrLen > result_rec.length)
						return ATTRTOOLONG;
					memcpy((char*)result_rec.data + result_offset, (char*)innerRec.data + attrDescArray[i].attrOffset, attrDescArray[i].attrLen);
					result_offset += attrDescArray[i].attrLen;
				}
				//outer check
				else if(strcmp(attrDesc2.relName, attrDescArray[i].relName) == 0)
				{
					//error check
					if(result_offset + attrDescArray[i].attrLen > result_rec.length)
						return ATTRTOOLONG;
					memcpy((char*)result_rec.data + result_offset, (char*)outerRec.data + attrDescArray[i].attrOffset, attrDescArray[i].attrLen);
					result_offset += attrDescArray[i].attrLen;
				}
				else if(i == (projCnt -1))
				{
					return ATTRNOTFOUND;
				}
			}	
			RID result_rid;
			Status insert_status = heapFileResult.insertRecord(result_rec, result_rid);
			if(insert_status != OK)
				return insert_status;

			free(result_rec.data);
			
		}	
		Status inner_scan_status = innerScan.endScan();
		if(inner_scan_status != OK)
			return inner_scan_status;
	}	
	outer_scan_status = outerScan.endScan();
	if(outer_scan_status != OK)
		return outer_scan_status;

  return OK;
}

